package com.rw.engine.sign.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import com.rw.tool.util.json.JsonUtil;
import com.rw.tool.util.string.StringFormatter;
import com.rw.tool.util.string.StringUtil;
import lombok.Getter;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

/**
 * 扁平化对象
 * 这个类可以让任意一个非基本类型的对象变为扁平的TreeMap
 * 这样方便后续计算签名
 */
@Getter
public class FlatMap {
    // 基本类型报错
    private static final String PRIMITIVE_EXCEPTION = "Flattened objects cannot be of primitive types! this object is {}";

    // 未知jsonNode类型报错
    private static final String UNKNOWN_EXCEPTION = "Unknown json node: {}";

    // 最终展开对象的结果
    private final TreeMap<String, String> result;

    /**
     * 展开对象
     *
     * @param obj 展开的对象不能为基础类型
     */
    public FlatMap(Object obj) {
        result = new TreeMap<>();
        JsonNode jsonNode = JsonUtil.getMapper().valueToTree(obj);

        // 如果展开对象为基础类型，则抛出异常
        if (jsonNode instanceof ValueNode) {
            throw new IllegalArgumentException(StringFormatter.format(PRIMITIVE_EXCEPTION, obj));
        }

        // 递归展开
        flatMap(StringUtil.EMPTY, jsonNode);
    }

    /**
     * 递归函数，扁平化当前节点
     * 如果得到了最终结果，就加入 result 中
     */
    public void flatMap(String name, JsonNode jsonNode) {
        if (jsonNode instanceof ObjectNode) {
            // 如果第一个jsonNode是对象类型
            ObjectNode objectNode = (ObjectNode) jsonNode;
            ObjectNodeFlatMap(name, objectNode);
        } else if (jsonNode instanceof ArrayNode) {
            // 如果第一个jsonNode是数组类型
            ArrayNode arrayNode = (ArrayNode) jsonNode;
            ArrayNodeFlatMap(name, arrayNode);
        } else if (jsonNode instanceof ValueNode) {
            // 如果第一个jsonNode是值类型
            ValueNode valueNode = (ValueNode) jsonNode;
            result.put(name, valueNode.asText());
        } else {
            // 未知的jsonNode类型
            throw new IllegalArgumentException(StringFormatter.format(UNKNOWN_EXCEPTION, jsonNode));
        }
    }

    /**
     * 处理对象类型的节点
     *
     * @param name       该节点的名字
     * @param objectNode 对象节点
     */
    private void ObjectNodeFlatMap(String name, ObjectNode objectNode) {
        Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
        // 遍历对象的所有属性
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> next = fields.next();
            // 合并父节点和这个节点的名字
            String childName = mergeName(name, next.getKey());
            // 进入递归，扁平化所有子节点
            flatMap(childName, next.getValue());
        }
    }

    /**
     * 处理数组类型的节点
     *
     * @param name      该节点的名字
     * @param arrayNode 数组节点
     */
    private void ArrayNodeFlatMap(String name, ArrayNode arrayNode) {
        // 遍历所有数组
        for (int i = 0; i < arrayNode.size(); i++) {
            JsonNode childNode = arrayNode.get(i);
            // 合并父节点和这个节点的名字
            final String indexTemplate = "{}[{}]";
            String childName = StringFormatter.format(indexTemplate, name, i);
            // 进入递归，扁平化所有子节点
            flatMap(childName, childNode);
        }
    }

    /**
     * 合并两级的名字
     *
     * @param parent 父级名字
     * @param child  子级名字
     * @return 合并的名字
     */
    private String mergeName(String parent, String child) {
        // 如果父节点为空，则说明是第一层，只需要子节点的名字即可
        if (StringUtil.isBlank(parent)) {
            return child;
        }
        final String mergeTemplate = "{}.{}";
        return StringFormatter.format(mergeTemplate, parent, child);
    }

    /**
     * 将一个对象扁平化，并且变为 TreeMap 返回
     * 注意，被扁平化的对象不能为基础类型，否则会抛出异常
     *
     * @param obj 被扁平化的对象
     * @return 扁平化的结果
     */
    public static TreeMap<String, String> flatMapObj(Object obj) {
        return new FlatMap(obj).getResult();
    }
}
